package MyExpiredLock

import (
	"context"
	"errors"
	"sync"
	"time"
)

type ExpiredLock struct {
	mutex sync.Mutex // 核心单机锁

	processMutex sync.Mutex // 辅助锁 保证加/解锁流程的原子性

	owner string // 标识锁的归属方

	stop context.CancelFunc //异步 goroutine 生命周期终止控制器
}

func NewExpiredLock() *ExpiredLock {
	return &ExpiredLock{}
}

func (e *ExpiredLock) Lock(expireSeconds int) {
	e.mutex.Lock()

	// 放在单机锁后 防止 辅助锁加锁之后，单机锁陷入阻塞 无法释放辅助锁
	e.processMutex.Lock()
	defer e.processMutex.Unlock()

	token := GetCurrentProcessAndGogroutineIDStr()
	e.owner = token

	if expireSeconds <= 0 {
		return
	}

	ctx, cancel := context.WithCancel(context.Background())
	e.stop = cancel

	// 保证在达到过期时长之后，执行解锁操作
	go func() {
		select {
		case <-time.After(time.Duration(expireSeconds) * time.Second):
			e.unlock(token)
		case <-ctx.Done():
		}
	}()
}

func (e *ExpiredLock) Unlock() error {
	token := GetCurrentProcessAndGogroutineIDStr()
	return e.unlock(token)
}

func (e *ExpiredLock) unlock(token string) error {
	e.processMutex.Lock()
	defer e.processMutex.Unlock()

	if token != e.owner {
		return errors.New("not your lock")
	}

	// 锁的归属方置为空
	e.owner = ""
	e.mutex.Unlock()
	// 停止加锁时的异步goroutine的生命周期
	if e.stop != nil {
		e.stop()
	}
	return nil
}
